use std::fmt::Debug;
use std::fs;
use std::io::Read;
use std::str::FromStr;

use opencv::core::{Mat, Vector, VectorToVec};
use opencv::hub_prelude::VideoCaptureTrait;
use opencv::imgcodecs::imencode;
use opencv::videoio;
use opencv::videoio::VideoCapture;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum InputError {
    #[error("Source parse error: {0}")]
    SourceParseError(String),
    #[error("Protocol not support: {0}")]
    ProtocolNotSupportError(String),
    #[error("Camera connect error: {0}")]
    CameraConnectError(String),
}

///the source of input image
pub enum Source {
    ///from local file
    FILE(String),
    ///from network file, support http or https, like `https://abc.com/image.png`
    HTTP(String),
    ///from rtsp stream, like `rtsp://192.168.1.2:554`
    RTSP(String, VideoCapture, Mat),
    ///from camera, like `camera://0`, `0` is camera device index
    CAMERA(i32, VideoCapture, Mat),
}

const SCHEMA_FILE: &str = "file://";
const SCHEMA_HTTP: &str = "http://";
const SCHEMA_HTTPS: &str = "https://";
const SCHEMA_RTSP: &str = "rtsp://";
const SCHEMA_CAMERA: &str = "camera://";

impl FromStr for Source {
    type Err = InputError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.starts_with(SCHEMA_FILE) {
            Ok(Source::FILE(s.replace(SCHEMA_FILE, "").to_string()))
        } else if s.starts_with(SCHEMA_HTTP) || s.starts_with(SCHEMA_HTTPS) {
            Ok(Source::HTTP(s.to_string()))
        } else if s.starts_with(SCHEMA_RTSP) {
            let camera =
                VideoCapture::from_file(s, videoio::CAP_ANY).expect("Camera connect error");
            let frame = Mat::default();
            Ok(Source::RTSP(s.to_string(), camera, frame))
        } else if s.starts_with(SCHEMA_CAMERA) {
            let index = s
                .replace(SCHEMA_CAMERA, "")
                .parse::<i32>()
                .expect("Camera index error");
            let camera = VideoCapture::new(index, videoio::CAP_ANY).expect("Camera connect error");
            let frame = Mat::default();
            Ok(Source::CAMERA(index, camera, frame))
        } else {
            Ok(Source::FILE(s.to_string()))
        }
    }
}

const IMAGE_EXT: &str = ".png";

pub fn get_image_bytes(source: &mut Source) -> eyre::Result<Vec<u8>> {
    match source {
        Source::FILE(file) => {
            let vec = fs::read(&file)?;
            Ok(vec)
        }
        Source::HTTP(url) => {
            let response = reqwest::blocking::get(url.as_str())?;
            let vec = response.bytes()?.to_vec();
            Ok(vec)
        }
        Source::RTSP(_, camera, frame) => {
            camera.read(frame)?;
            let mut vec = Vector::new();
            imencode(IMAGE_EXT, frame, &mut vec, &Vector::new())?;
            Ok(vec.to_vec())
        }
        Source::CAMERA(_, camera, frame) => {
            camera.read(frame)?;
            let mut vec = Vector::new();
            imencode(IMAGE_EXT, frame, &mut vec, &Vector::new())?;
            Ok(vec.to_vec())
        }
    }
}
